/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package webcamtracking;

import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

/**
 *
 * @author James
 */
public class ImageProcessor {

    private Grid grid;

    public BufferedImage detectBackGround(BufferedImage referenceFrame, BufferedImage currentFrame) {
        BufferedImage workingImage = new BufferedImage(currentFrame.getWidth(), currentFrame.getHeight(), currentFrame.getType());
        Graphics2D g = workingImage.createGraphics();
        return workingImage;
    }

    public BufferedImage backgroundDifference(BufferedImage previousFrame, BufferedImage currentFrame, int threshold) {
        BufferedImage workingImage = new BufferedImage(currentFrame.getWidth(), currentFrame.getHeight(), currentFrame.getType());
        Graphics2D g = workingImage.createGraphics();
        for (int x = 0; x < currentFrame.getWidth(); x++) {
            for (int y = 0; y < currentFrame.getHeight(); y++) {
                int RGBcolor = currentFrame.getRGB(x, y);
                if (matchPixel(x, y, previousFrame, RGBcolor, threshold)) {
                    g.setColor(Color.WHITE);
                } else {
                    g.setColor(new Color(RGBcolor));
                }
                g.drawRect(x, y, 1, 1);
            }
        }
        return workingImage;
    }

    public BufferedImage populateWithEdgeBoxes(BufferedImage rawImage, int gridSize, int threshold) {
        //TODO: refactor to one method with AreaBoxes
        //split into grid of x size
        //for each square in grid count black pixels        
        grid = new Grid(gridSize, gridSize);
        BufferedImage workingImage = rawImage;
        Graphics2D g = workingImage.createGraphics();
        int step = (int) (rawImage.getWidth() / gridSize);

        for (int i = 0; i < (rawImage.getWidth() / step); i++) {
            for (int j = 0; j < (rawImage.getHeight() / step); j++) {

                if (getNumberOfBlackPixelsInBounds(rawImage, i * step, j * step, step) <= threshold) {
                    GridElement e = new GridElement();
                    e.setIsActive(true);
                    grid.set(i, j, e);

                } else {
                    GridElement e = new GridElement();
                    e.setIsActive(false);
                    grid.set(i, j, e);
                }
            }
        }

        GridElement[][] gridArray = grid.toArray();
        int headID = identifyHead(gridArray, gridSize);
        int rightHandID = identifyRightHand(gridArray, gridSize);
        int leftHandID = identifyLeftHand(gridArray, gridSize);

        for (int i = 0; i < (gridSize); i++) {
            for (int j = 0; j < (gridSize); j++) {
                if (gridArray[i][j] != null) {
                    if (gridArray[i][j].isActive()) {
                        if (gridArray[i][j].getId() == headID) {
                            g.setColor(Color.GREEN);
                            g.drawRect(i * step, j * step, step, step);
                        //g.drawString(gridArray[i][j].getId() + "", i * step, j * step);
                        } else if (gridArray[i][j].getId() == rightHandID && getIDCount(gridArray, rightHandID) <= gridSize) {
                            g.setColor(Color.RED);
                            g.drawRect(i * step, j * step, step, step);
                        } else if (gridArray[i][j].getId() == leftHandID && getIDCount(gridArray, leftHandID) <= gridSize) {
                            g.setColor(Color.BLUE);
                            g.drawRect(i * step, j * step, step, step);
                        }

                    }
                }
            }
        }
        return workingImage;
    }

    public BufferedImage populateWithAreaBoxes(BufferedImage rawImage, int gridSize, int threshold) {
        //TODO: refactor to one method with EdgeBoxes
        //split into grid of x size
        //for each square in grid count black pixels        
        grid = new Grid(gridSize, gridSize);
        BufferedImage workingImage = rawImage;
        Graphics2D g = workingImage.createGraphics();
        int step = (int) (rawImage.getWidth() / gridSize);

        for (int i = 0; i < (rawImage.getWidth() / step); i++) {
            for (int j = 0; j < (rawImage.getHeight() / step); j++) {

                if (getNumberOfWhitePixelsInBounds(rawImage, i * step, j * step, step) >= threshold) {
                    GridElement e = new GridElement();
                    e.setIsActive(true);
                    grid.set(i, j, e);

                } else {
                    GridElement e = new GridElement();
                    e.setIsActive(false);
                    grid.set(i, j, e);
                }
            }
        }

        GridElement[][] gridArray = grid.toArray();
        int headID = identifyHead(gridArray, gridSize);
        int rightHandID = identifyRightHand(gridArray, gridSize);
        int leftHandID = identifyLeftHand(gridArray, gridSize);

        for (int i = 0; i < (gridSize); i++) {
            for (int j = 0; j < (gridSize); j++) {
                if (gridArray[i][j] != null) {
                    if (gridArray[i][j].isActive()) {
                        if (gridArray[i][j].getId() == headID) {
                            g.setColor(Color.GREEN);
                            g.drawRect(i * step, j * step, step, step);
                        //g.drawString(gridArray[i][j].getId() + "", i * step, j * step);
                        } else {
                            g.setColor(Color.RED);
                            g.drawRect(i * step, j * step, step, step);
                        }
                    }
                }
            }
        }
        return workingImage;
    }

    public BufferedImage populateWithSquares(BufferedImage rawImage, int gridSize, int threshold) {
        //split into grid of x size
        //for each square in grid count black pixels        
        grid = new Grid(gridSize, gridSize);
        BufferedImage workingImage = rawImage;
        Graphics2D g = workingImage.createGraphics();
        int step = (int) (rawImage.getWidth() / gridSize);

        for (int i = 0; i < (rawImage.getWidth() / step); i++) {
            for (int j = 0; j < (rawImage.getHeight() / step); j++) {

                if (getNumberOfWhitePixelsInBounds(rawImage, i * step, j * step, step) >= threshold) {
                    GridElement e = new GridElement();
                    e.setIsActive(true);
                    grid.set(i, j, e);

                } else {
                    GridElement e = new GridElement();
                    e.setIsActive(false);
                    grid.set(i, j, e);
                }
            }
        }

        GridElement[][] gridArray = grid.toArray();
        int headID = identifyHead(gridArray, gridSize);
        int rightHandID = identifyRightHand(gridArray, gridSize);
        int leftHandID = identifyLeftHand(gridArray, gridSize);

        for (int i = 0; i < (gridSize); i++) {
            for (int j = 0; j < (gridSize); j++) {
                if (gridArray[i][j] != null) {
                    if (gridArray[i][j].isActive()) {
                        if (gridArray[i][j].getId() == headID) {
                            g.setColor(Color.GREEN);
                            g.drawRect(i * step, j * step, step, step);
                        //g.drawString(gridArray[i][j].getId() + "", i * step, j * step);
                        } else if (gridArray[i][j].getId() == rightHandID && getIDCount(gridArray, rightHandID) <= gridSize) {
                            g.setColor(Color.RED);
                            g.drawRect(i * step, j * step, step, step);
                        } else if (gridArray[i][j].getId() == leftHandID && getIDCount(gridArray, leftHandID) <= gridSize) {
                            g.setColor(Color.BLUE);
                            g.drawRect(i * step, j * step, step, step);
                        }

                    }
                }
            }
        }
        return workingImage;
    }

    private int getIDCount(GridElement[][] gridArray, int gridID) {

        int count = 0;
        for (int i = 0; i < gridArray.length; i++) {
            for (int j = 0; j < gridArray[0].length; j++) {
                if (gridArray[i][j].getId() == gridID) {
                    count++;
                }
            }
        }
        return count;
    }

    private int identifyRightHand(GridElement[][] gridArray, int gridSize) {
        int gridHalf = gridSize / 2;
        int rightHandID = gridArray[gridHalf / 2 / 2][gridHalf].getId();
        return rightHandID;
    }

    private int identifyLeftHand(GridElement[][] gridArray, int gridSize) {
        //TODO
        //find all groups on right hand side of screen 
        //that are of specific size
        //identify hand
        //return value
        int gridHalf = gridSize / 2;
        int leftHandID = gridArray[gridSize - gridHalf / 2][gridHalf].getId();

        return leftHandID;
    }

    private int identifyHead(GridElement[][] gridArray, int gridSize) {
        int gridHalf = gridSize / 2;
        int headID = gridArray[gridHalf][gridHalf].getId();
        return headID;
    }

    private int identifyLargestGroup(GridElement[][] gridArray) {

        HashMap<Integer, Integer> groupCount = new HashMap<Integer, Integer>();
        ArrayList<Integer> elementIDs = new ArrayList<Integer>();

        for (int i = 0; i < gridArray.length; i++) {
            for (int j = 0; j < gridArray[0].length; j++) {
                if (gridArray[i][j].getId() != 0) {
                    if (groupCount.containsKey(gridArray[i][j].getId())) {
                        int buffer = groupCount.get(gridArray[i][j].getId());
                        buffer++;
                        groupCount.put(gridArray[i][j].getId(), buffer);
                    } else {
                        groupCount.put(gridArray[i][j].getId(), 1);
                        elementIDs.add(gridArray[i][j].getId());
                    }
                }
            }
        }

        //get ID of largest count
        Iterator<Integer> it = elementIDs.iterator();
        int id1 = it.next();
        int count1 = groupCount.get(id1);
        while (it.hasNext()) {
            int id2 = it.next();
            int count2 = groupCount.get(id2);
            if (count2 > count1) {
                id1 = id2;
                count1 = count2;
            }
        }

        return id1;

    }

    private int getNumberOfBlackPixelsInBounds(BufferedImage rawImage, int x, int y, int size) {
        int[] rgbArray = new int[size * size];
        int count = 0;
        rawImage.getRGB(x, y, size, size, rgbArray, 0, size);
        for (int i = 0; i < rgbArray.length; i++) {
            Color c = new Color(rgbArray[i]);
            if (c.equals(Color.BLACK)) {
                count++;
            }
        }
        return count;

    }

    private int getNumberOfWhitePixelsInBounds(BufferedImage rawImage, int x, int y, int size) {
        int[] rgbArray = new int[size * size];
        int count = 0;
        rawImage.getRGB(x, y, size, size, rgbArray, 0, size);
        for (int i = 0; i < rgbArray.length; i++) {
            Color c = new Color(rgbArray[i]);
            if (c.equals(Color.WHITE)) {
                count++;
            }
        }
        return count;

    }

    public BufferedImage colorDetection(BufferedImage rawImage, int RGBcolor, int threshold) {
        //Pixel by pixel:
        //take a pixel...does it match color...writeback
        BufferedImage workingImage = new BufferedImage(rawImage.getWidth(), rawImage.getHeight(), rawImage.getType());
        Graphics2D g = workingImage.createGraphics();
        for (int x = 0; x < rawImage.getWidth(); x++) {
            for (int y = 0; y < rawImage.getHeight(); y++) {
                if (matchPixel(x, y, rawImage, RGBcolor, threshold)) {
                    g.setColor(Color.white);
                } else {
                    g.setColor(Color.black);
                }
                g.drawRect(x, y, 1, 1);
            }
        }
        return workingImage;
    }

    public BufferedImage edgeDetection(BufferedImage rawImage, int threshold) { //image to mod
        //Pixel by pixel:
        //take a pixel...modify...write back   

        BufferedImage workingImage = new BufferedImage(rawImage.getWidth(), rawImage.getHeight(), rawImage.getType());
        Graphics2D g = workingImage.createGraphics();
        for (int x = 0; x < rawImage.getWidth(); x++) {
            for (int y = 0; y < rawImage.getHeight(); y++) {
                if (matchSurround(x, y, rawImage, threshold)) {
                    g.setColor(Color.white);
                } else {
                    g.setColor(Color.black);
                }
                g.drawRect(x, y, 1, 1);
            }
        }
        g.dispose();
        return workingImage;
    }

    private boolean matchSurround(int x, int y, BufferedImage img, int threshold) {

        Pixel centre = getPixel(x, y, img);
        int c = centre.getRGB();

        if ((x > 1 && y > 1) && (x < img.getWidth() - 1 && y < img.getHeight() - 1)) {
            if (getPixel(x - 1, y - 1, img).isSimilar(c, threshold) &&
                    getPixel(x, y - 1, img).isSimilar(c, threshold) &&
                    getPixel(x + 1, y - 1, img).isSimilar(c, threshold) &&
                    getPixel(x - 1, y, img).isSimilar(c, threshold) &&
                    getPixel(x + 1, y, img).isSimilar(c, threshold) &&
                    getPixel(x - 1, y + 1, img).isSimilar(c, threshold) &&
                    getPixel(x, y + 1, img).isSimilar(c, threshold) &&
                    getPixel(x + 1, y + 1, img).isSimilar(c, threshold)) {
                return true;
            } else {
                return false;
            }
        } else {
            return false;
        }

    }

    private boolean matchPixel(int x, int y, BufferedImage rawImage, int RGBcolor, int threshold) {
        return getPixel(x, y, rawImage).isSimilar(RGBcolor, threshold);
    }

    private Pixel getPixel(int x, int y, BufferedImage img) {
        if (x < 0 || y < 0) {
            return null;
        } else if (x > img.getWidth() || y > img.getHeight()) {
            return null;
        } else {
            Pixel p = new Pixel(img.getRGB(x, y));
            return p;
        }

    }
}
